package nachos.userprog;

import nachos.machine.*;
import nachos.threads.*;

import java.io.EOFException;
import java.util.ArrayList;
/**
 * Encapsulates the state of a user process that is not contained in its
 * user thread (or threads). This includes its address translation state, a
 * file table, and information about the program being executed.
 *
 * <p>
 * This class is extended by other classes to support additional functionality
 * (such as additional syscalls).
 *
 * @see	nachos.vm.VMProcess
 * @see	nachos.network.NetProcess
 */
public class UserProcess {
	/**
	 * Allocate a new process.
	 */
	public UserProcess() {
	int numPhysPages = Machine.processor().getNumPhysPages();
	pageTable = new TranslationEntry[numPhysPages];
	for (int i=0; i<numPhysPages; i++)
		pageTable[i] = new TranslationEntry(-1,-1, false,false,false,false);
	
	fileDescriptorTracker[0] = UserKernel.console.openForReading();
	fileDescriptorStatusTracker[0] = 1;
	fileDescriptorTracker[1] = UserKernel.console.openForWriting();
	fileDescriptorStatusTracker[1] = 1;

	
	//Part 3
	//processID = ((UserKernel)Kernel.kernel).insertTree(-1, this);
	processID = nextAvailablePid++;
	//End part 3

	System.out.println("DERRRRRRRRRRRRRRP");
	}
	
	/**
	 * Allocate and return a new process of the correct class. The class name
	 * is specified by the <tt>nachos.conf</tt> key
	 * <tt>Kernel.processClassName</tt>.
	 *
	 * @return	a new process of the correct class.
	 */
	public static UserProcess newUserProcess() {
	return (UserProcess)Lib.constructObject(Machine.getProcessClassName());
	}

	/**
	 * Execute the specified program with the specified arguments. Attempts to
	 * load the program, and then forks a thread to run it.
	 *
	 * @param	name	the name of the file containing the executable.
	 * @param	args	the arguments to pass to the executable.
	 * @return	<tt>true</tt> if the program was successfully executed.
	 */
	public boolean execute(String name, String[] args) {
	if (!load(name, args)){
		unloadSections();
		return false;
	}
	cThread = new UThread(this); // current thread
	cThread.setName(name).fork();
	processBeingUsed++;
	return true;
	}

	/**
	 * Save the state of this process in preparation for a context switch.
	 * Called by <tt>UThread.saveState()</tt>.
	 */
	public void saveState() {
	}

	/**
	 * Restore the state of this process after a context switch. Called by
	 * <tt>UThread.restoreState()</tt>.
	 */
	public void restoreState() {
	Machine.processor().setPageTable(pageTable);
	}

	/**
	 * Read a null-terminated string from this process's virtual memory. Read
	 * at most <tt>maxLength + 1</tt> bytes from the specified address, search
	 * for the null terminator, and convert it to a <tt>java.lang.String</tt>,
	 * without including the null terminator. If no null terminator is found,
	 * returns <tt>null</tt>.
	 *
	 * @param	vaddr	the starting virtual address of the null-terminated
	 *			string.
	 * @param	maxLength	the maximum number of characters in the string,
	 *				not including the null terminator.
	 * @return	the string read, or <tt>null</tt> if no null terminator was
	 *		found.
	 */
    public String readVirtualMemoryString(int vaddr, int maxLength) {
        Lib.assertTrue(maxLength >= 0);

        byte[] bytes = new byte[maxLength+1];

        int bytesRead = readVirtualMemory(vaddr, bytes);

        for (int length=0; length<bytesRead; length++) {
                if (bytes[length] == 0)
                        return new String(bytes, 0, length);
        }

        return null;
    }


	/**
	 * Transfer data from this process's virtual memory to all of the specified
	 * array. Same as <tt>readVirtualMemory(vaddr, data, 0, data.length)</tt>.
	 *
	 * @param	vaddr	the first byte of virtual memory to read.
	 * @param	data	the array where the data will be stored.
	 * @return	the number of bytes successfully transferred.
	 */
	public int readVirtualMemory(int vaddr, byte[] data) {
	return readVirtualMemory(vaddr, data, 0, data.length);
	}

	/**
	 * Transfer data from this process's virtual memory to the specified array.
	 * This method handles address translation details. This method must
	 * <i>not</i> destroy the current process if an error occurs, but instead
	 * should return the number of bytes successfully copied (or zero if no
	 * data could be copied).
	 *
	 * @param	vaddr	the first byte of virtual memory to read.
	 * @param	data	the array where the data will be stored.
	 * @param	offset	the first byte to write in the array.
	 * @param	length	the number of bytes to transfer from virtual memory to
	 *			the array.
	 * @return	the number of bytes successfully transferred.
	 */
	
	public int readVirtualMemory(int vaddr, byte[] data, int offset,
				 int length) {
		Lib.assertTrue(offset >= 0 && length >= 0 && offset+length <= data.length);
	
		byte[] memory = Machine.processor().getMemory();
		
		if (vaddr < 0)
			return 0;
	
		/**************************************************
		 *   Part II - Adding Multiprogramming Support
		 *   readVirtualMemory(int vaddr, byte[] data, int offset,
		 *      int length
		 **************************************************/
		
		int firstPage = Processor.pageFromAddress(vaddr);
		int lastPage = Processor.pageFromAddress(vaddr + length);
		int pageOffset = vaddr % pageSize;
		int arrayOffset = offset;
		int numberOfMiddlePages = lastPage - firstPage - 1;
		int amount = 0;	// amount of data processed
		int ppn = 0; // physical page number
		int paddr = 0; // physical address
		
		// Case 1: First Page
		ppn = pageTable[firstPage].ppn;
		paddr = Processor.makeAddress(ppn, pageOffset);
		if (firstPage != lastPage) {	// there exists more than one page
			System.arraycopy(memory, paddr, data, arrayOffset, pageSize - pageOffset);
			
			// Update variables
			length -= (pageSize - pageOffset);
			arrayOffset += (pageSize - pageOffset);
			amount += arrayOffset;
		}
		else {	// there exists only one page
			System.arraycopy(memory, paddr, data, arrayOffset, length);
			
			return length;
		}
		
		// Case 2: Middle Pages
		if (numberOfMiddlePages > 0) {	// there exists at least one page in the middle
			for (int i = 0; i < numberOfMiddlePages; i++) {
				ppn = pageTable[firstPage + i].ppn;
				paddr = Processor.makeAddress(ppn, 0);
				System.arraycopy(memory, paddr, data, arrayOffset, pageSize);
				
				// Update variables
				length -= pageSize;
				arrayOffset += pageSize;
				amount += pageSize;
			}
		}
		
		// Case 3: Last Page
		ppn = pageTable[lastPage].ppn;
		paddr = Processor.makeAddress(ppn, 0);
		System.arraycopy(memory, paddr, data, arrayOffset, length);
	
		// Update amount variable
		amount += pageSize;
		
		
		//int amount = Math.min(length, memory.length-vaddr);
		//System.arraycopy(memory, vaddr, data, offset, amount);
		return amount;
		/**************************************************/
	}
	
	/**
	 * Transfer all data from the specified array to this process's virtual
	 * memory.
	 * Same as <tt>writeVirtualMemory(vaddr, data, 0, data.length)</tt>.
	 *
	 * @param	vaddr	the first byte of virtual memory to write.
	 * @param	data	the array containing the data to transfer.
	 * @return	the number of bytes successfully transferred.
	 */
	public int writeVirtualMemory(int vaddr, byte[] data) {
	return writeVirtualMemory(vaddr, data, 0, data.length);
	}

	/**
	 * Transfer data from the specified array to this process's virtual memory.
	 * This method handles address translation details. This method must
	 * <i>not</i> destroy the current process if an error occurs, but instead
	 * should return the number of bytes successfully copied (or zero if no
	 * data could be copied).
	 *
	 * @param	vaddr	the first byte of virtual memory to write.
	 * @param	data	the array containing the data to transfer.
	 * @param	offset	the first byte to transfer from the array.
	 * @param	length	the number of bytes to transfer from the array to
	 *			virtual memory.
	 * @return	the number of bytes successfully transferred.
	 */

	
	
	
	public int writeVirtualMemory(int vaddr, byte[] data, int offset,
				  int length) {
		Lib.assertTrue(offset >= 0 && length >= 0 && offset+length <= data.length);
	
		byte[] memory = Machine.processor().getMemory();
		
		if (vaddr < 0)
			return 0;
	
	
		/**************************************************
		 *   Part II - Adding Multiprogramming Support
		 *   writeVirtualMemory(int vaddr, byte[] data, int offset,
		 *      int length)
		 **************************************************/
	
		int firstPage = Processor.pageFromAddress(vaddr);
		int lastPage = Processor.pageFromAddress(vaddr + length);
		int pageOffset = vaddr % pageSize;
		int arrayOffset = offset;
		int numberOfMiddlePages = lastPage - firstPage - 1;
		int amount = 0;	// amount of data processed
		int ppn = 0; // physical page number
		int paddr = 0; // physical address
		
		// Case 1: First Page
		ppn = pageTable[firstPage].ppn;
		paddr = Processor.makeAddress(ppn, pageOffset);
		if (firstPage != lastPage) {	// there exists more than one page
			System.arraycopy(data, arrayOffset, memory, paddr, pageSize - pageOffset);
			
			// Update variables
			length -= (pageSize - pageOffset);
			arrayOffset += (pageSize - pageOffset);
			amount += arrayOffset;
		}
		else {	// there exists only one page
			System.arraycopy(data, arrayOffset, memory, paddr, length);
			
			return length;
		}
		
		// Case 2: Middle Pages
		if (numberOfMiddlePages > 0) {	// there exists at least one page in the middle
			for (int i = 0; i < numberOfMiddlePages; i++) {
				ppn = pageTable[firstPage + i].ppn;
				paddr = Processor.makeAddress(ppn, 0);
				System.arraycopy(data, arrayOffset, memory, paddr, pageSize);
				
				// Update variables
				length -= pageSize;
				arrayOffset += pageSize;
				amount += pageSize;
			}
		}
		
		// Case 3: Last Page
		ppn = pageTable[lastPage].ppn;
		paddr = Processor.makeAddress(ppn, 0);
		System.arraycopy(data, arrayOffset, memory, paddr, length);
	
		// Update amount variable
		amount += pageSize;
		
		//int amount = Math.min(length, memory.length-vaddr);
		//System.arraycopy(data, offset, memory, vaddr, amount);
		return amount;
		/**************************************************/
	}

	/**
	 * Load the executable with the specified name into this process, and
	 * prepare to pass it the specified arguments. Opens the executable, reads
	 * its header information, and copies sections and arguments into this
	 * process's virtual memory.
	 *
	 * @param	name	the name of the file containing the executable.
	 * @param	args	the arguments to pass to the executable.
	 * @return	<tt>true</tt> if the executable was successfully loaded.
	 */
	private boolean load(String name, String[] args) {
	Lib.debug(dbgProcess, "UserProcess.load(\"" + name + "\")");
	
	OpenFile executable = ThreadedKernel.fileSystem.open(name, false);
	if (executable == null) {
		Lib.debug(dbgProcess, "\topen failed");
		return false;
	}

	try {
		coff = new Coff(executable);
	}
	catch (EOFException e) {
		executable.close();
		Lib.debug(dbgProcess, "\tcoff load failed");
		return false;
	}

	// make sure the sections are contiguous and start at page 0
	numPages = 0;
	for (int s=0; s<coff.getNumSections(); s++) {
		CoffSection section = coff.getSection(s);
		if (section.getFirstVPN() != numPages) {
		coff.close();
		Lib.debug(dbgProcess, "\tfragmented executable");
		return false;
		}
		numPages += section.getLength();
	}

	// make sure the argv array will fit in one page
	byte[][] argv = new byte[args.length][];
	int argsSize = 0;
	for (int i=0; i<args.length; i++) {
		argv[i] = args[i].getBytes();
		// 4 bytes for argv[] pointer; then string plus one for null byte
		argsSize += 4 + argv[i].length + 1;
	}
	if (argsSize > pageSize) {
		coff.close();
		Lib.debug(dbgProcess, "\targuments too long");
		return false;
	}

	// program counter initially points at the program entry point
	initialPC = coff.getEntryPoint();	

	// next comes the stack; stack pointer initially points to top of it
	numPages += stackPages;
	initialSP = numPages*pageSize;

	// and finally reserve 1 page for arguments
	numPages++;

	if (!loadSections())
		return false;

	// store arguments in last page
	int entryOffset = (numPages-1)*pageSize;
	int stringOffset = entryOffset + args.length*4;

	this.argc = args.length;
	this.argv = entryOffset;
	
	for (int i=0; i<argv.length; i++) {
		byte[] stringOffsetBytes = Lib.bytesFromInt(stringOffset);
		Lib.assertTrue(writeVirtualMemory(entryOffset,stringOffsetBytes) == 4);
		entryOffset += 4;
		Lib.assertTrue(writeVirtualMemory(stringOffset, argv[i]) ==
			   argv[i].length);
		stringOffset += argv[i].length;
		Lib.assertTrue(writeVirtualMemory(stringOffset,new byte[] { 0 }) == 1);
		stringOffset += 1;
	}

	return true;
	}

	/**
	 * Allocates memory for this process, and loads the COFF sections into
	 * memory. If this returns successfully, the process will definitely be
	 * run (this is the last step in process initialization that can fail).
	 *
	 * @return	<tt>true</tt> if the sections were successfully loaded.
	 */
	protected boolean loadSections() {
		if (numPages > Machine.processor().getNumPhysPages()) {
			coff.close();
			Lib.debug(dbgProcess, "\tinsufficient physical memory");
			return false;
		}
		
		// Allocate memory for process
		for (int i = 0; i < numPages; i++) {
			TranslationEntry translationEntry = pageTable[i];
			if (translationEntry == null)
				return false;
			
			int ppn = ((UserKernel)Kernel.kernel).getPage();
			if (ppn < 0 || ppn > Machine.processor().getNumPhysPages())
				return false;
			
			pageTable[i].ppn = ppn;
			pageTable[i].valid = true;
		}
		
		// load sections
		for (int s=0; s<coff.getNumSections(); s++) {
			CoffSection section = coff.getSection(s);
			
			Lib.debug(dbgProcess, "\tinitializing " + section.getName()
				  + " section (" + section.getLength() + " pages)");
	
			for (int i=0; i<section.getLength(); i++) {
				int vpn = section.getFirstVPN()+i;
				int ppn = pageTable[vpn].ppn;
				if (ppn < 0)
					return false;
				
				section.loadPage(i, ppn);
			}
		}
	
		return true;
	}
			

	/**
	 * Release any resources allocated by <tt>loadSections()</tt>.
	 */
	protected void unloadSections() {
		/**************************************************
		 *   Part II - Adding Multiprogramming Support
		 *   unloadSections()
		 **************************************************/
		
		for (int i = 0; i < numPages; i++) {
			//if (i > 0 && i <= pageTable.length && pageTable[i].valid){
			if(pageTable[i].valid)
				((UserKernel)Kernel.kernel).freePage(pageTable[i].ppn);
		}
		System.out.println("ending unload");
		/**************************************************/
	}	

	/**
	 * Initialize the processor's registers in preparation for running the
	 * program loaded into this process. Set the PC register to point at the
	 * start function, set the stack pointer register to point at the top of
	 * the stack, set the A0 and A1 registers to argc and argv, respectively,
	 * and initialize all other registers to 0.
	 */
	public void initRegisters() {
	Processor processor = Machine.processor();

	// by default, everything's 0
	for (int i=0; i<Processor.numUserRegisters; i++)
		processor.writeRegister(i, 0);

	// initialize PC and SP according
	processor.writeRegister(Processor.regPC, initialPC);
	processor.writeRegister(Processor.regSP, initialSP);

	// initialize the first two argument registers to argc and argv
	processor.writeRegister(Processor.regA0, argc);
	processor.writeRegister(Processor.regA1, argv);
	}

	
	/**
	 * Handle the halt() system call. 
	 */
	private int handleHalt() {

	Machine.halt();
	
	Lib.assertNotReached("Machine.halt() did not halt machine!");
	return 0;
	}
	
	/*************************************
	 * PART I: a bunch of stuff
	 * 
	 **************************************/
	//---------------------------------
	//vma = virtual memory address
	//fd = file descriptor
	protected int creatOrOpen(int vma, boolean check){
		int newFileDescriptor = -1;
		if (vma < 0) {
			return -1;
		}
		String fileName = readVirtualMemoryString(vma, 255);		
		if (fileName == null) 
			return -1; 
		OpenFile newFile = ThreadedKernel.fileSystem.open(fileName,check);
		if (newFile == null)
			return -1;
		
		for (int i = 0; i < fileDescriptorStatusTracker.length; i++) {  //loops through and checks the next available spot
			if (fileDescriptorStatusTracker[i] == 0){
				newFileDescriptor =  i; break;}
		}
		if (newFileDescriptor < 0)
			return -1;

		fileDescriptorTracker[newFileDescriptor] = newFile;
		fileDescriptorStatusTracker[newFileDescriptor] =1;
		return newFileDescriptor;	
	}
	protected int read(int fd, int vma, int bytesRead ){
		byte[] tempBuffer = new byte[bytesRead];
		if(fd < 0 || vma < 0)
			return -1;
		OpenFile accessedFile = fileDescriptorTracker[fd];
		int virtualPageNumber = Processor.pageFromAddress(vma);
		if(accessedFile == null || pageTable[virtualPageNumber].readOnly == true)
			return -1;
	//needs to make sure it's the only one accessed file at a time
			int bytesBeingRead = accessedFile.read(tempBuffer, 0,bytesRead); //returns the "actual" bytes being read, returns -1 of fails
			if(bytesBeingRead == -1){
				return -1;
			}
			int transferedBytes = writeVirtualMemory(vma, tempBuffer);
		if(transferedBytes == 0)
			return -1;
		return bytesBeingRead;
		
	}
	protected int write(int fd, int vma, int streamBytesWrite){
		byte[] tempBuffer = new byte[streamBytesWrite];
		if(fd < 0 || vma < 0)
			return -1;
		OpenFile accessedFile = fileDescriptorTracker[fd];
		if(accessedFile == null)
			return -1;
		
			readVirtualMemory(vma,tempBuffer,0,streamBytesWrite);
			int bytesBeingWritten = accessedFile.write(tempBuffer, 0, streamBytesWrite);

		
		if(bytesBeingWritten < streamBytesWrite)
			return -1;
		return bytesBeingWritten;
	}
	
	protected int close(int fd){
		if(fd < 0)
			return -1;
		OpenFile accessedFile = fileDescriptorTracker[fd];
		if (accessedFile == null || fileDescriptorStatusTracker[fd] == 0)
			return -1;
		//To state that the current space used is free (memory available again)
		fileDescriptorStatusTracker[fd] = 0; 
		fileDescriptorTracker[fd] = null;
		accessedFile.close();
		return 0;
	}
	protected int unlink(int vma){
		if(vma < 0)
			return -1;
		String fileAccessed = readVirtualMemoryString(vma, 256);
		if(fileAccessed == null)
			return -1;
		//I'm assuming that the current implementation is checking if the current file is being opened or not
		//with in the stubFileSystem.java
		boolean removed = ThreadedKernel.fileSystem.remove(fileAccessed); 
		if(!removed)
			return -1;
		
		return 0;
	}

	
	//-----------------
	//Part 3
	//-----------------
	protected UThread cThread;


	protected int exec(int fileAdd, int numArg, int argvAdd) {

	/*	System.out.println("exec got called");

		//Create new node and set parent
		UserProcess blah = newUserProcess();
		((UserKernel)Kernel.kernel).setParent(blah.processID,processID);

		//Collect arguments, filename, etc.
		String fileName = readVirtualMemoryString(fileAdd,256);
		String[] arguments = new String[numArg]; 
		byte[] argAdd = new byte[numArg * 4];
		readVirtualMemory(argvAdd,argAdd);
		for(int i=0; i<numArg; i++)
		{
			arguments[i] = readVirtualMemoryString(Lib.bytesToInt(argAdd,  i*4), 256);
		}
		
		//nullify if false execute, confirm this process has resulted in an error
		if(blah.execute(fileName,arguments) == false)
		{
			((UserKernel)Kernel.kernel).errorFailedCall(blah.processID);
			return -1;    		
		}
	 	*/
		System.out.println("exec got called");
		String fileName = readVirtualMemoryString(fileAdd,256);
  		// Checks the validity of the input
        if(fileName == null || numArg < 0 || !fileName.endsWith(".coff")) {
                return exit(1);
        }
        
        //a way to save existing arguments.
        String[] args = new String[0];
        if(numArg != 0) {
        	// First load the pointer array.
        	byte[] argArr = new byte[numArg*4];
        	args = new String[numArg];
        	if(readVirtualMemory(argvAdd, argArr) != numArg*4) {  //if there are errors loading it in
        		return -1;
        	}
        	// Then load the actual arguments
            for(int i = 0; i < numArg; ++i) {
                int actualArg = Lib.bytesToInt(argArr, i*4);
                args[i] = readVirtualMemoryString(actualArg, 256);
                if(args[i] == null) {
                        return exit(1);
                }
            }
        }
        // makes a new child
        UserProcess newChild = new UserProcess();
        boolean executeChecker = newChild.execute(fileName, args);
        if(executeChecker == true) {
            children.add(newChild);
            return newChild.processID;
        } else {
            return -1;
        }
		//return blah.processID;
	}


	protected int join(int pID, int status) {
		
	/*	System.out.println("testing");

		if (((UserKernel)Kernel.kernel).isThere(child) == false)
			return -1;
		if (((UserKernel)Kernel.kernel).findParent(child) != processID)
			return -1;
		if (((UserKernel)Kernel.kernel).checkError(child))
			return 0;
		//Joins it using KThread's join
		if (((UserKernel)Kernel.kernel).checkRunning(child))
			((UserKernel)Kernel.kernel).getProc(child).cThread.join();

		if (((UserKernel)Kernel.kernel).checkError(child))
			return 0;
		//Prevents it from joining a second time.
		((UserKernel)Kernel.kernel).setParent(child,-1);

		int retval  = ((UserKernel)Kernel.kernel).getRet(child);
		writeVirtualMemory(status,Lib.bytesFromInt(retval));
		return 1;*/


		if(pID < 0)
		{
			return 0;
		}
        UserProcess child = null;                        
        for(int i = 0; i < children.size(); i++) {
        	if(children.get(i).processID == pID){
                        child = children.get(i);
                        break;
                }
        }
        //checks if it's not a child
        if(child == null) 
        	return -1;
        
        //loops until child finishes
        while(!child.finished) 
        	UThread.currentThread().yield();
        

        children.remove(child);
        byte[] dataAmount = Lib.bytesFromInt(child.returnCode);

        int trueDataSize = writeVirtualMemory(status, dataAmount);
        if (trueDataSize != dataAmount.length) 
                return exit(1);
        
                return 1;


	}

	protected int exit(int returnVal) {
/*		//Free the space.
		System.out.println("Exit got called");
		unloadSections();

		System.out.println("exit compelted part 1");
		// inform all children parent is exiting, and set everything in the node correctly.
		for(int i = 0; i < fileDescriptorTracker.length; i++)
		{    		
			int value;
			if (fileDescriptorStatusTracker[i] != 0) 
				 value = close(i);
		}

		((UserKernel)Kernel.kernel).setFinish(processID, returnVal);


		System.out.println("exit compelted part 2");
		
		
		//Check if it's the last one. If so, terminate.
	if (((UserKernel)Kernel.kernel).last(processID) == true){
			System.out.println("dO i termintate stuff?");
			Kernel.kernel.terminate();}

		//If not, close this one and end.
		KThread.currentThread().finish();

		
		System.out.println("exit compelted");
		return 0;
*/
         // Free the space
         unloadSections();

        //Close opened files
		for(int i = 0; i < fileDescriptorTracker.length; i++)
		{    		
			if (fileDescriptorStatusTracker[i] != 0) 
				  close(i);
		}
                //Check if it's the last one. If so, terminate.
                --processBeingUsed;
                if(processBeingUsed == 0) {
                        Kernel.kernel.terminate();
                }

                finished = true;
                returnCode = returnVal;
                KThread.currentThread().finish();
                return returnCode;
	}
	
	private static final int
	syscallHalt = 0,
	syscallExit = 1,
	syscallExec = 2,
	syscallJoin = 3,
	syscallCreate = 4,
	syscallOpen = 5,
	syscallRead = 6,
	syscallWrite = 7,
	syscallClose = 8,
	syscallUnlink = 9;

	/**
	 * Handle a syscall exception. Called by <tt>handleException()</tt>. The
	 * <i>syscall</i> argument identifies which syscall the user executed:
	 *
	 * <table>
	 * <tr><td>syscall#</td><td>syscall prototype</td></tr>
	 * <tr><td>0</td><td><tt>void halt();</tt></td></tr>
	 * <tr><td>1</td><td><tt>void exit(int status);</tt></td></tr>
	 * <tr><td>2</td><td><tt>int  exec(char *name, int argc, char **argv);
	 * 								</tt></td></tr>
	 * <tr><td>3</td><td><tt>int  join(int processID, int *status);</tt></td></tr>
	 * <tr><td>4</td><td><tt>int  creat(char *name);</tt></td></tr>
	 * <tr><td>5</td><td><tt>int  open(char *name);</tt></td></tr>
	 * <tr><td>6</td><td><tt>int  read(int fd, char *buffer, int size);
	 *								</tt></td></tr>
	 * <tr><td>7</td><td><tt>int  write(int fd, char *buffer, int size);
	 *								</tt></td></tr>
	 * <tr><td>8</td><td><tt>int  close(int fd);</tt></td></tr>
	 * <tr><td>9</td><td><tt>int  unlink(char *name);</tt></td></tr>
	 * </table>
	 * 
	 * @param	syscall	the syscall number.
	 * @param	a0	the first syscall argument.
	 * @param	a1	the second syscall argument.
	 * @param	a2	the third syscall argument.
	 * @param	a3	the fourth syscall argument.
	 * @return	the value to be returned to the user.
	 */
	public int handleSyscall(int syscall, int a0, int a1, int a2, int a3) {
	switch (syscall) {
	case syscallHalt:
		return handleHalt();
	case syscallCreate:
		return creatOrOpen(a0, true);
	case syscallOpen:
		return creatOrOpen(a0, false);
	case syscallRead:
		return read(a0, a1, a2);
	case syscallWrite:
		return write(a0,a1,a2);
	case syscallClose:
		return close(a0);
	case syscallUnlink:
		return unlink(a0);
	case syscallExit:
		return exit(a0);
	case syscallExec:
		return exec(a0,a1,a2);
	case syscallJoin:
		return join(a0,a1);

	default:
		Lib.debug(dbgProcess, "Unknown syscall " + syscall);
		Lib.assertNotReached("Unknown system call!");
	}
	return 0;
	}

	/**
	 * Handle a user exception. Called by
	 * <tt>UserKernel.exceptionHandler()</tt>. The
	 * <i>cause</i> argument identifies which exception occurred; see the
	 * <tt>Processor.exceptionZZZ</tt> constants.
	 *
	 * @param	cause	the user exception that occurred.
	 */
	public void handleException(int cause) {
	Processor processor = Machine.processor();

	switch (cause) {
	case Processor.exceptionSyscall:
		int result = handleSyscall(processor.readRegister(Processor.regV0),
					   processor.readRegister(Processor.regA0),
					   processor.readRegister(Processor.regA1),
					   processor.readRegister(Processor.regA2),
					   processor.readRegister(Processor.regA3)
					   );
		processor.writeRegister(Processor.regV0, result);
		processor.advancePC();
		break;					   
					   
	default:
		Lib.debug(dbgProcess, "Unexpected exception: " +
			  Processor.exceptionNames[cause]);
		Lib.assertNotReached("Unexpected exception");
	}
	}

	public void selfTest(){
		/*
		UserProcess mem = new UserProcess();
		mem.numPages = 3;
		mem.pageTable[0] = new TranslationEntry(0,1,true,false,false,false);
		mem.pageTable[1] = new TranslationEntry(1,0,true,false,false,false);
		
		int vaddr = Processor.makeAddress(1, 0);
		System.out.println("Virtual address is : " + Integer.toBinaryString(vaddr));
		int wint = Integer.MAX_VALUE;
		int rint = 0;
		byte[] wbuffer = new byte[1000];
		byte[] rbuffer = new byte[500];
		Lib.bytesFromInt(wbuffer,0,wint);
		mem.writeVirtualMemory(vaddr, wbuffer);
		mem.readVirtualMemory(vaddr, rbuffer);
		rint = Lib.bytesToInt(rbuffer, 0);
		boolean pass = true;
		if(rint != wint){
			pass = false;
			System.out.println("FAIL");
		}
		else{
			System.out.println("PASS");
		}*/
		
		//UserProcess part3Test = new UserProcess();
		//part3Test.exit(1);
		//part3Test.exec(1, 255, 4);
	//	part3Test.join(child, status);	
	}
	
	
	/** The program being run by this process. */
	protected Coff coff;

	/** This process's page table. */
	protected TranslationEntry[] pageTable;
	/** The number of contiguous pages occupied by the program. */
	protected int numPages;

	/** The number of pages in the program's stack. */
	protected final int stackPages = 8;
	
	private int initialPC, initialSP;
	private int argc, argv;
	
	private static final int pageSize = Processor.pageSize;
	private static final char dbgProcess = 'a';
	
	
	
	//------------------------newly added variables-----------------
		public static int openFileAmount = 16;
		protected OpenFile[] fileDescriptorTracker = new OpenFile[openFileAmount];
		public int[] fileDescriptorStatusTracker = new int[openFileAmount];
	//----part 3 test stuff	
		ArrayList<UserProcess> children = new ArrayList<UserProcess>();
		int processID = -1;
	    static int nextAvailablePid = 1;
	    static int processBeingUsed = 0;
	    protected boolean finished = false;
	    int returnCode = 0;

}
